package net.nutss.stunt;

import java.net.*;
import java.nio.ByteBuffer;
import java.io.*;
import net.nutss.STUNTSelector;

;

public class STUNTSocket extends Thread {

	public Socket s = null;
	public STUNTCont con = null;
	public PipedInputStream is;
	PipedOutputStream os;
	Exception er = null;
	volatile boolean stop = false;
	int il = 0;
	volatile boolean bIsConnectEvent = false;
	volatile boolean bToSetConnect=false;
	private STUNTSelector sel=null;
	private SocketAddress toConnect=null;
	

	public boolean hasData() {
		boolean bret = true;
		// for some kinds of socket, it must set a connect event
		if (bIsConnectEvent) {
			bIsConnectEvent = false;
			return true;
		}
		try {
			bret = is.available() > 0;
		} catch (Exception e) {
			er = e;
		}
		return bret;
	}

	public boolean isError() {
		return er != null;
	}

	public void stopSelect() {
		if (!stop) {
			stop = true;
			try {
				Thread.sleep(110);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				// e.printStackTrace();
			}
		}
	}

	public void run() {
		if(toConnect!=null)
		{
			try
			{
				s.connect(toConnect);
			}
			catch(Exception e)
			{
				if(sel!=null)sel.remove(this);
				return;
			}
		}
		if(bToSetConnect && s.isConnected())bIsConnectEvent = true;
		stop = false;
		er = null;
		try {
			il = s.getSoTimeout();
			s.setSoTimeout(25);
		} catch (Exception e) {
			er = e;
			stop = true;
			return;
		}
		while (!stop && er == null) {
			if (!canRead()) {
				stop = true;
				return;
			}

			try {
				byte[] ab = new byte[256];
				int i = this.s.getInputStream().read(ab);
				if (i > 0)
					this.os.write(ab, 0, i);
			} catch (SocketTimeoutException et) {
			} catch (Exception e) {
				er = e;
			}
		}
		try {
			s.setSoTimeout(il);
		} catch (Exception e) {
		}
		if(sel!=null)sel.remove(this);
	}
	public STUNTSocket(Socket s, STUNTCont con,STUNTSelector sel,SocketAddress remote,boolean btoConnet) {
		toConnect=remote;
		bToSetConnect=btoConnet;
		is = new PipedInputStream();
		try {
			os = new PipedOutputStream(is);
		} catch (Exception e) {
		}
		this.s = s;
		this.con = con;
		this.sel=sel;
		if(sel!=null)sel.add(this);
	}

	public STUNTSocket(Socket s, STUNTCont con,STUNTSelector sel) {

		bToSetConnect=false;
		is = new PipedInputStream();
		try {
			os = new PipedOutputStream(is);
		} catch (Exception e) {
		}
		this.s = s;
		this.con = con;
		this.sel=sel;
		if(sel!=null)sel.add(this);
	}

	public int readInt() throws IOException {
		if (is.available() >= 4) {
			ByteBuffer inbuf = ByteBuffer.allocateDirect(4);
			byte[] b = new byte[4];
			if (is.read(b) == 4) {
				inbuf.put(b);
				inbuf.flip();
				return inbuf.getInt();
			}
		}
		throw new java.io.IOException("Not a validate socket");
	}

	public int read() throws IOException {
		if (is.available() > 0)
			return is.read();
		throw new java.io.IOException("Not a validate socket");
	}

	public boolean isConnected() {
		return s != null && s.isConnected();
	}

	public void writeInt(int val) throws IOException {
		if (canWrite()) {
			ByteBuffer outbuf = ByteBuffer.allocate(4);
			outbuf.putInt(val);
			outbuf.flip();
			s.getOutputStream().write(outbuf.array());
			s.getOutputStream().flush();
		}
	}

	public void close() throws IOException {
		stopSelect();
		if (s != null && !s.isClosed())
			s.close();
		if(sel!=null)sel.remove(this);
	}

	public boolean canWrite() {
		return s != null && s.isConnected() && !s.isClosed()
				&& !s.isOutputShutdown();
	}

	public boolean canRead() {
		return s != null && s.isConnected() && !s.isClosed()
				&& !s.isInputShutdown();
	}
}
